package com.lagou.edu.scan;

import com.lagou.edu.annotation.*;
import com.lagou.edu.factory.ProxyFactory;
import com.lagou.edu.pojo.ComponentAnnotationValue;
import com.lagou.edu.utils.ClassUtil;
import com.lagou.edu.utils.TransactionManager;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.SQLException;
import java.util.*;

public class AnnotationApplicationContext {

    private ProxyFactory proxyFactory;

    private static Map<String,Object> objects = new HashMap<>();  // 存储对象

    public AnnotationApplicationContext(Class<?> iocConfigClass) throws Exception {
        init(iocConfigClass);
    }

    public void init(Class<?> iocConfigClass) throws Exception {
        ComponentScan componentScan = iocConfigClass.getAnnotation(ComponentScan.class);
        //只对实现了ComponentScan的配置类进行处理
        if(null == componentScan){
            throw new RuntimeException("this class unrealized ComponentScan annotation ");
        }
        String basePackage = componentScan.value();
        //获取 包下所有类信息
        List<Class> classes = ClassUtil.getClasssFromPackage(basePackage);
        List<Class> unmodifiableList = Collections.unmodifiableList(classes);

        newInstanceComponent(unmodifiableList);

    }

    /**
     * 初始化所有组件类
     * @param classes 扫描包下的所有类信息
     * @throws Exception
     */
    private void newInstanceComponent(List<Class> classes)  throws Exception{
        for (Class aClass : classes) {
            //类是否实现了service 或 repository 或 component 注解
            ComponentAnnotationValue componentAnnotationValue = getComponentAnnotationValue(aClass);
            if(!componentAnnotationValue.isMatch()){
                continue;
            }
            String annoValue = componentAnnotationValue.getAnnoValue();
            Object instance = aClass.newInstance();
            //如果设置了别名就用设置好的，没有设置就用类名
            String key = annoValue.isEmpty() ? lowerFirst(aClass.getSimpleName()) : annoValue;
            objects.put(key,instance);
        }

        proxyFactory = getBean("proxyFactory");
        dependencyInjection();
        transationAop();
    }

    /**
     * 字段依赖注入
     * @throws Exception
     */
    private void dependencyInjection()  throws Exception{
        for(Map.Entry<String,Object> entry : objects.entrySet()){
            Object instance = entry.getValue();
            Class<?> instanceClass = instance.getClass();
            for (Field field : instance.getClass().getDeclaredFields()) {
                Autowired autowiredAnnotation = field.getAnnotation(Autowired.class);
                if(null != autowiredAnnotation){
                    Object value = objects.get(field.getName());
                    if(value == null){
                        throw new RuntimeException("not find class for autowired");
                    }
                    //使用暴力访问，完成依赖封装
                    field.setAccessible(true);
                    field.set(instance,value);
                }
            }
        }
    }


    /**
     * 生成代理对象
     */
    private void transationAop(){
        for(Map.Entry<String,Object> entry : objects.entrySet()){
            Object instance = entry.getValue();
            Class<?> instanceClass = instance.getClass();
            Object o = null;
            // 获取代理对象
            if(instanceClass.getGenericInterfaces().length == 0){
                o = proxyFactory.getCglibProxy(instance);
            }else {
                o = proxyFactory.getJdkProxy(instance);
            }
            objects.put(entry.getKey(),o);
        }
    }

    private Object invokeInTransation(Object instance,Method method, Object[] args,TransactionManager transactionManager) throws Exception {
        Object result = null;
        try{
            // 开启事务(关闭事务的自动提交)
            transactionManager.beginTransaction();

            result = method.invoke(instance,args);

            // 提交事务

            transactionManager.commit();
        }catch (Exception e) {
            e.printStackTrace();
            // 回滚事务
            transactionManager.rollback();

            // 抛出异常便于上层servlet捕获
            throw e;

        }
        return result;
    }

    private ComponentAnnotationValue getComponentAnnotationValue(Class<?> aClass){
        ComponentAnnotationValue componentAnnotationValue = new ComponentAnnotationValue();
        String annoValue = "";
        if(aClass.isAnnotationPresent(Service.class)){
            Service componentAnnotation = aClass.getAnnotation(Service.class);
            annoValue = componentAnnotation.value();
        }else if (aClass.isAnnotationPresent(Repository.class)){
            Repository componentAnnotation = aClass.getAnnotation(Repository.class);
            annoValue = componentAnnotation.value();
        }else if (aClass.isAnnotationPresent(Component.class)){
            Component componentAnnotation = aClass.getAnnotation(Component.class);
            annoValue = componentAnnotation.value();
        }else {
            componentAnnotationValue.setMatch(false);
        }
        componentAnnotationValue.setAnnoValue(annoValue);
        return componentAnnotationValue;
    }

    private String lowerFirst(String name){
        char[] chars = name.toCharArray();
        chars[0] += 32;
        return String.valueOf(chars);
    }

    public static <T> T getBean(String name){
        return (T) objects.get(name);
    }

}
